﻿using Microsoft.Win32;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Ocsp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Management;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace LicenseTools
{
    public class LicenseClient
    {
        /// <summary>
        /// 根据硬件信息生成机器序列号
        /// </summary>
        /// <returns></returns>
        public static string GenSerialNo()
        {
            try
            {
                //获取硬件信息
                string info = string.Empty;
                string cpu = GetCPUInfo();
                string gpu = GetGPUInfo();
                string baseBoard = GetBaseBoardInfo();
                string bios = GetBIOSInfo();
                string mac = GetMACInfo();
                info = string.Concat(cpu, gpu, baseBoard, bios, mac);

                //AES加密
                using (Aes tempAES = Aes.Create())
                {
                    byte[] encrypted = EncryptStringToBytes(info, tempAES.Key, tempAES.IV);
                    byte[] dataCnt = BitConverter.GetBytes(Convert.ToInt16(encrypted.Length));

                    byte[] res = new byte[tempAES.Key.Length
                                        + tempAES.IV.Length
                                        + dataCnt.Length
                                        + encrypted.Length];

                    //拼接AES密钥 32+16 bytes
                    Buffer.BlockCopy(tempAES.Key, 0, res, 0, tempAES.Key.Length);
                    Buffer.BlockCopy(tempAES.IV, 0, res, tempAES.Key.Length, tempAES.IV.Length);
                    //拼接加密数据
                    Buffer.BlockCopy(dataCnt, 0, res, tempAES.Key.Length + tempAES.IV.Length, dataCnt.Length);
                    Buffer.BlockCopy(encrypted, 0, res, tempAES.Key.Length + tempAES.IV.Length + dataCnt.Length, encrypted.Length);

                    //生成机器序列号
                    info = Convert.ToBase64String(res);
                }
                return info;
            }
            catch (Exception)
            {
                return null;
            }
        
        }



        /// <summary>
        /// 校验授权文件
        /// </summary>
        /// <param name="license"></param>
        /// <param name="publicKey"></param>
        /// <returns></returns>
        public  static bool VerifyLicense(string license, string publicKey)
        {
            try
            {
                byte[] lic = Convert.FromBase64String(license);

                //获取AES密钥信息
                byte[] aesKey = lic.Skip(0).Take(32).ToArray();
                byte[] aesIV = lic.Skip(32).Take(16).ToArray();

                //获取加密数据长度
                int dataCnt = BitConverter.ToInt16(lic, 48);

                //获取加密数据
                byte[] data = lic.Skip(50).Take(dataCnt).ToArray();

                //获取签名
                byte[] sign = lic.Skip(50 + dataCnt).Take(128).ToArray();
                string signStr = Convert.ToBase64String(sign);
                //Console.WriteLine("get sign: " + signStr);

                //获取时间
                byte[] times = lic.Skip(48 + 2 + dataCnt + 128).ToArray();
                string[] timeStrs = DecryptStringFromBytes(times, aesKey, aesIV).Split('|');
                //byte[] start = lic.Skip(50 + dataCnt+128).Take(8).ToArray();
                //byte[] end = lic.Skip(50 + dataCnt + 128+8).Take(8).ToArray();
                //byte[] last = lic.Skip(50 + dataCnt + 128+16).Take(8).ToArray();


                //获取签名的对象
                byte[] sn = lic.Skip(0).Take(50 + dataCnt).ToArray();
                string snStr = Convert.ToBase64String(sn);
                //Console.WriteLine("get sn: " + snStr);

                //验签
                if (VerifySign(snStr, signStr, publicKey) == false)
                    return false;

                //校验机器序列号
                if (CheckHardware(snStr, aesKey, aesIV) == false)
                    return false;

                //校验授权时间
                if (CheckTime(timeStrs[0], timeStrs[1], timeStrs[2]) == false)
                    return false;

                return true;
            }
            catch (Exception)
            {

                return false;
            }

        }



        /// <summary>
        /// 更新登录时间
        /// </summary>
        /// <param name="license"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool UpdateVerifyTime(string license,string path)
        {
            try
            {
                byte[] lic = Convert.FromBase64String(license);
                
                byte[] now = BitConverter.GetBytes(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds());
                int dataCnt = BitConverter.ToInt16(lic, 48);
                byte[] aesKey = lic.Skip(0).Take(32).ToArray();
                byte[] aesIV = lic.Skip(32).Take(16).ToArray();
                byte[] times = lic.Skip(48 + 2 + dataCnt + 128).ToArray();
                string[] timeStr = DecryptStringFromBytes(times, aesKey, aesIV).Split('|');
             
                long nowTime = BitConverter.ToInt64(now, 0);
                long lastTime = Convert.ToInt64(timeStr[2]);

                //时间只能递增
                if (lastTime >= nowTime)
                    return true;


                timeStr[2] = nowTime.ToString();
                byte[] newtimes = EncryptStringToBytes(timeStr[0]+"|"+ timeStr[1] + "|" + timeStr[2], aesKey, aesIV);
                byte[] newlic = new byte[48+2+dataCnt+128+ newtimes.Length];
                Buffer.BlockCopy(lic, 0, newlic, 0, 48 + 2 + dataCnt + 128);
                Buffer.BlockCopy(newtimes, 0, newlic, 48 + 2 + dataCnt + 128, newtimes.Length);

                license = Convert.ToBase64String(newlic);
                using (StreamWriter writer = new StreamWriter(path))
                {
                    writer.WriteLine(license);
                }

                return true;
            }
            catch (Exception)
            {
                return false;
            }

        }



            #region private methods

            /// <summary>
            /// 检查硬件信息
            /// </summary>
            /// <param name="sn"></param>
            /// <param name="key"></param>
            /// <param name="Iv"></param>
            /// <returns></returns>
            private static bool CheckHardware(string sn, byte[] key, byte[] Iv)
        {
            //获取硬件信息
            string info = string.Empty;
            string cpu = GetCPUInfo();
            string gpu = GetGPUInfo();
            string baseBoard = GetBaseBoardInfo();
            string bios = GetBIOSInfo();
            string mac = GetMACInfo();
            info = string.Concat(cpu, gpu, baseBoard, bios, mac);

            //AES加密
            byte[] encrypted = EncryptStringToBytes(info, key, Iv);
            byte[] dataCnt = BitConverter.GetBytes(Convert.ToInt16(encrypted.Length));

            byte[] res = new byte[key.Length
                                + Iv.Length
                                + dataCnt.Length
                                + encrypted.Length];

            //拼接AES密钥 32+16 bytes
            Buffer.BlockCopy(key, 0, res, 0, key.Length);
            Buffer.BlockCopy(Iv, 0, res, key.Length, Iv.Length);
            //拼接加密数据
            Buffer.BlockCopy(dataCnt, 0, res, key.Length + Iv.Length, dataCnt.Length);
            Buffer.BlockCopy(encrypted, 0, res, key.Length + Iv.Length + dataCnt.Length, encrypted.Length);

            //生成机器序列号
            info = Convert.ToBase64String(res);

            if (info != sn)
                return false;
            return true;
        }


        /// <summary>
        /// 检查授权时间
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="last"></param>
        /// <returns></returns>
        private static bool CheckTime(string startTime, string endTime, string lastTime)
        {
            long now =  new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
            long start = Convert.ToInt64(startTime);
            long end = Convert.ToInt64(endTime);
            long last = Convert.ToInt64(lastTime);

            if (end<=start)
                return false;
            if (now < start || now >= end)
                return false;
            if(now<= last) 
                return false;

            return true;
        }



        /// <summary>
        /// 最大解密长度
        /// </summary>
        private const int MAX_DECRYPT_BLOCK = 256;


        /// <summary>
        /// 公钥解密
        /// </summary>
        /// <param name="xmlPublicKey"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        private static string Decrypt(string xmlPublicKey, string content)
        {
            //加载公钥
            RSACryptoServiceProvider publicRsa = new RSACryptoServiceProvider();
            publicRsa.FromXmlString(xmlPublicKey);
            RSAParameters rp = publicRsa.ExportParameters(false);

            //转换密钥
            AsymmetricKeyParameter pbk = DotNetUtilities.GetRsaPublicKey(rp);

            IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding");
            //第一个参数为true表示加密，为false表示解密；第二个参数表示密钥
            c.Init(false, pbk);

            byte[] DataToDecrypt = Convert.FromBase64String(content);

            byte[] cache;
            int time = 0;//次数
            int inputLen = DataToDecrypt.Length;
            int offSet = 0;
            MemoryStream outStream = new MemoryStream();
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK)
                {
                    cache = c.DoFinal(DataToDecrypt, offSet, MAX_DECRYPT_BLOCK);
                }
                else
                {
                    cache = c.DoFinal(DataToDecrypt, offSet, inputLen - offSet);
                }
                //写入
                outStream.Write(cache, 0, cache.Length);

                time++;
                offSet = time * MAX_DECRYPT_BLOCK;
            }
            byte[] resData = outStream.ToArray();

            string strDec = Encoding.UTF8.GetString(resData);
            return strDec;

        }


        /// <summary>
        /// 公钥验签
        /// </summary>
        /// <param name="str">待验证的字符串</param>
        /// <param name="sign">加签之后的字符串</param>
        /// <returns>签名是否符合</returns>
        private static bool VerifySign(string str, string sign, string publicKey)
        {

            byte[] bt = Convert.FromBase64String(str);
            byte[] rgbHash = null;

            var csp = new SHA256CryptoServiceProvider();
            rgbHash = csp.ComputeHash(bt);

            RSACryptoServiceProvider key = new RSACryptoServiceProvider();
            key.FromXmlString(publicKey);
            RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key);
            deformatter.SetHashAlgorithm("SHA256");
            byte[] rgbSignature = Convert.FromBase64String(sign);
            if (deformatter.VerifySignature(rgbHash, rgbSignature))
                return true;
            return false;
        }


        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="plainText"></param>
        /// <param name="Key"></param>
        /// <param name="IV"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");
            byte[] encrypted;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;
        }




        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="cipherText"></param>
        /// <param name="Key"></param>
        /// <param name="IV"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }

            return plaintext;
        }



        /// <summary>
        /// 获取CPU序列号
        /// </summary>
        /// <returns></returns>
        private static string GetCPUInfo()
        {
            var info = string.Empty;
            var mc = new ManagementClass("Win32_Processor");
            var moc = mc.GetInstances();
            foreach (var o in moc)
            {
                var mo = (ManagementObject)o;
                info = mo.Properties["ProcessorId"].Value.ToString();
            }
            return info;
        }

        /// <summary>
        /// 获取显卡序列号
        /// </summary>
        /// <returns></returns>
        private static string GetGPUInfo()
        {
            var info = "";
            var mos = new ManagementObjectSearcher("Select * from Win32_VideoController");
            foreach (var o in mos.Get())
            {
                var mo = (ManagementObject)o;
                info = mo["PNPDeviceID"].ToString();
            }
            return info;
        }

        /// <summary>
        /// 获取主板序列号
        /// </summary>
        /// <returns></returns>
        private static string GetBaseBoardInfo()
        {
            var info = string.Empty;
            var mos = new ManagementObjectSearcher("Select * from Win32_BaseBoard");
            foreach (var o in mos.Get())
            {
                var mo = (ManagementObject)o;
                info = mo["SerialNumber"].ToString();
            }
            return info;
        }

        /// <summary>
        /// 获取BIOS序列号
        /// </summary>
        /// <returns></returns>
        private static string GetBIOSInfo()
        {
            var info = string.Empty;
            var mos = new ManagementObjectSearcher("Select * from Win32_BIOS");
            foreach (var o in mos.Get())
            {
                var mo = (ManagementObject)o;
                info = mo["SerialNumber"].ToString();
            }
            return info;
        }


        /// <summary>
        /// 获取MAC地址
        /// </summary>
        /// <returns></returns>
        private static string GetMACInfo()
        {
            var info = string.Empty;
            var mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
            var moc = mc.GetInstances();
            foreach (var o in moc)
            {
                var mo = (ManagementObject)o;
                if (!(bool)mo["IPEnabled"]) continue;
                info = mo["MacAddress"].ToString();
                break;
            }
            return info;
        }

        #endregion


    }
}
